﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Abp.Dependency;
using Abp.Domain.Repositories;
using Abp.Runtime.Caching;
using DHJ.Cost.Workshops;
using Microsoft.EntityFrameworkCore;

namespace DHJ.Cost.CostAccounting.Cache
{
    public class CostAccountingCache : ISingletonDependency, ICostAccountingCache
    {
        private readonly ICacheManager _cacheManager;
        private readonly IRepository<AllocRel, Guid> _allocRelRepository;

        public CostAccountingCache(ICacheManager cacheManager, IRepository<AllocRel, Guid> allocRelRepository)
        {
            _cacheManager = cacheManager;
            _allocRelRepository = allocRelRepository;
        }

        public async Task<CostAccountingCacheItem> GetCacheItemOrNull(Guid id)
        {
            return await _cacheManager
                .GetCache(CostAccountingCacheItem.CacheName)
                .GetOrDefaultAsync(id.ToString()) as CostAccountingCacheItem;
        }

        /// <summary>
        /// 清空 items
        /// </summary>
        /// <returns></returns>
        public async Task ClearCache()
        {
            await _cacheManager
                .GetCache(CostAccountingCacheItem.CacheName).ClearAsync();
        }


        /// <summary>
        /// 初始化 items
        /// </summary>
        /// <returns></returns>
        public async Task InitCostAccountingCache()
        {
            await ClearCache();

            var items = await _allocRelRepository.GetAll().Include(e => e.ActualCost)
                .GroupBy(e => new {e.BatchId, e.Quantity})
                .Select(e => new CostAccountingCacheItem()
                {
                    Id = Guid.NewGuid(),
                    BatchId = e.Key.BatchId,

                    Quantity = e.Key.Quantity,
                    TotalCost = e.Sum(c => c.UnitCost * c.Quantity),
                    UnitItems = e.Select(c => new CostUnitCacheItem()
                    {
                        CostType = c.ActualCost.Detail.Category,
                        CostTitle = c.ActualCost.Detail.Title,
                        AllocRelId = c.Id,
                        CostValue = c.UnitCost * e.Key.Quantity,
                        Workshop = c.Workshop,
                        Year = c.ActualCost.Time.Year,
                        Month = c.ActualCost.Time.Month
                    }).ToList()
                }).ToListAsync();

            var cache = _cacheManager.GetCache(CostAccountingCacheItem.CacheName);

            cache.DefaultSlidingExpireTime = TimeSpan.FromDays(90);

            await cache.SetAsync("keys", items.Select(e => e.Id).ToList());

            foreach (var item in items)
            {
                await cache.SetAsync(item.Id.ToString(), item);
            }
        }

        /// <summary>
        /// 初始化 年度 items
        /// </summary>
        /// <returns></returns>
        public async Task InitAnnualCostCache()
        {
            var years = await _allocRelRepository.GetAll().Include(e => e.ActualCost)
                .GroupBy(e => e.ActualCost.Time.Year).Select(g => g.Key).ToListAsync();

            var cache = _cacheManager.GetCache(AnnualCostCacheItem.CacheName);

            cache.Clear();

            cache.DefaultSlidingExpireTime = TimeSpan.FromDays(90);

            await cache.SetAsync("keys", years);

            var items = await _allocRelRepository.GetAll().Include(e => e.ActualCost)
                .GroupBy(e => new {e.ActualCost.Time.Year, e.ActualCost.Detail.Category})
                .Select(g => new AnnualCostCacheItem()
                {
                    Category = g.Key.Category,
                    Year = g.Key.Year,
                    Cost = g.Sum(a => a.UnitCost * a.Quantity)
                }).ToListAsync();

            foreach (var year in years)
            {
                if (year == DateTime.Now.Year)
                    await InitAnnualCostCacheItem(cache, year, items);
                else
                {
                    var result = await cache.GetOrDefaultAsync<int, List<AnnualCostCacheItem>>(year);
                    if (result == null)
                        await InitAnnualCostCacheItem(cache, year, items);
                }
            }
        }

        private async Task InitAnnualCostCacheItem(ICache cache, int year, List<AnnualCostCacheItem> items)
        {
            var newItems = new List<AnnualCostCacheItem>();
            if (items != null && items.Count != 0)
                newItems = items.Where(e => e.Year == year).ToList();

            await cache.SetAsync(year.ToString(), newItems);
        }

        /// <summary>
        /// 初始化 车间 items
        /// </summary>
        /// <returns></returns>
        public async Task InitWorkshopCostCache()
        {

            var cache = _cacheManager.GetCache(WorkshopCostCacheItem.CacheName);

            cache.Clear();

            cache.DefaultSlidingExpireTime = TimeSpan.FromDays(90);

            var items = await _allocRelRepository.GetAll().Include(e => e.ActualCost)
                .Where(e => e.ActualCost.Workshop.HasValue)
                .GroupBy(e => new {e.ActualCost.Workshop, e.ActualCost.Detail.Category})
                .Select(g => new WorkshopCostCacheItem()
                {
                    Category = g.Key.Category,
                    Workshop = g.Key.Workshop.Value,
                    Cost = g.Sum(a => a.UnitCost * a.Quantity)
                }).ToListAsync();

            var workshops = items.GroupBy(e => e.Workshop).Select(g => g.Key).ToList();

            await cache.SetAsync("keys", workshops);

            foreach (var workshop in workshops)
            {
                await cache.SetAsync(workshop.ToString(), items.Where(e => e.Workshop == workshop).ToList());
            }
        }

        /// <summary>
        /// 获得所有的 keys
        /// </summary>
        /// <returns></returns>
        public async Task<List<Guid>> GetCacheKeys()
        {
            return await _cacheManager
                .GetCache(CostAccountingCacheItem.CacheName)
                .GetOrDefaultAsync("keys") as List<Guid>;
        }

        /// <summary>
        /// 获得所有的 items
        /// </summary>
        /// <returns></returns>
        public async Task<List<CostAccountingCacheItem>> GetCacheItems()
        {
            var keys = await GetCacheKeys();

            if (keys == null || keys.Count == 0)
                return null;

            var cache = _cacheManager
                .GetCache(CostAccountingCacheItem.CacheName);

            return keys.Select(key => cache.Get(key, () => new CostAccountingCacheItem()
            {
                Id = Guid.Empty
            })).Where(e => e.Id != Guid.Empty).ToList();
        }

        /// <summary>
        /// 通过 keys 获得对应的 items
        /// 一般用于分页获取
        /// </summary>
        /// <param name="keys"></param>
        /// <returns></returns>
        public async Task<List<CostAccountingCacheItem>> GetCacheItems(List<Guid> keys)
        {
            if (keys == null || keys.Count == 0)
                return null;


            var cache = _cacheManager
                .GetCache(CostAccountingCacheItem.CacheName);

            return keys.Select(key => cache.Get(key, () => new CostAccountingCacheItem()
            {
                Id = Guid.Empty
            })).Where(e => e.Id != Guid.Empty).ToList();
        }

        public async Task<List<List<AnnualCostCacheItem>>> GetAnnualCacheItems()
        {
            var cache = _cacheManager
                .GetCache(AnnualCostCacheItem.CacheName);

            var keys = await cache
                .GetOrDefaultAsync("keys") as List<int>;

            if (keys == null || keys.Count == 0)
                return null;

            return keys.Select(key => cache.Get<int, List<AnnualCostCacheItem>>(key, () => null))
                .Where(e => e != null).ToList();
        }

        public async Task<List<AnnualCostCacheItem>> GetAnnualCacheItems(int year)
        {
            return await _cacheManager
                       .GetCache(AnnualCostCacheItem.CacheName)
                       .GetOrDefaultAsync(year.ToString()) as List<AnnualCostCacheItem> ??
                   new List<AnnualCostCacheItem>();
        }

        public async Task<List<List<WorkshopCostCacheItem>>> GetWorkshopCacheItems()
        {
            var cache = _cacheManager
                .GetCache(WorkshopCostCacheItem.CacheName);

            var keys = await cache
                .GetOrDefaultAsync("keys") as List<int>;

            if (keys == null || keys.Count == 0)
                return null;

            return keys.Select(key => cache.Get<int, List<WorkshopCostCacheItem>>(key, () => null))
                .Where(e => e != null).ToList();
        }

        public async Task<List<WorkshopCostCacheItem>> GetWorkshopCacheItems(Workshop workshop)
        {
            return await _cacheManager
                       .GetCache(WorkshopCostCacheItem.CacheName)
                       .GetOrDefaultAsync(workshop.ToString()) as List<WorkshopCostCacheItem> ??
                   new List<WorkshopCostCacheItem>();
        }
    }
}